#include <QCoreApplication>

#include <fcntl.h>
#include <cstring>
#include <thread>
#include <vector>

#include <fstream>
#include <iostream>
#include <chrono>
#ifdef Q_OS_WIN
#include <windows.h>
#include <io.h>     // for _commit on Windows
#endif
#ifdef Q_OS_LINUX
#include <unistd.h> // for fsync on Unix-like systems
#endif

#include <QDateTime>
#include <QDebug>
#include <QFile>

// 可将这3个常量做到界面上，然后进行测试
// 写nas时，使用4线程一次写入时耗时最短，如写入512M，采用4线程单次写入128M方案时耗时最短
// 写本地磁盘时，使用4线程单次写入1M时耗时最短。
// 定义每个线程的缓冲区大小
const size_t kTotalSize = size_t(512) * 1024 * 1024;
const int kWriteSize = 128 * 1024 * 1024; // 设置单次写入大小为1MB
const int kThreadNum = 4; // 根据CPU核心数调整

#define STL_STREAM 1

/**
 * @brief writeThread
 * @param of 写入设备
 * @param buffer 源数据向量
 * @param threadId 线程id
 */
void writeThread(std::ofstream& of, size_t offset, size_t size, int threadId)
{
    // 填充数据，每个线程的数据不同
    std::vector<char> buffer(size, 'A' + threadId);
    // 计算写入次数
    auto write_num = buffer.size() / kWriteSize;
    of.seekp(offset);

    for (int i = 0; i < write_num; ++i)
    {
        of.write(buffer.data() + i * kWriteSize, kWriteSize);
    }

    auto remaining = buffer.size() - kWriteSize * write_num;

    if (remaining != 0)
    {
        of.write(buffer.data() + kWriteSize * write_num, remaining);
    }
    qDebug() << "thread ok" << threadId << ", time:" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}

void writeThreadQt(QFile &f, size_t offset, size_t size, int threadId)
{
    // 填充数据，每个线程的数据不同
    std::vector<char> buffer(size, 'A' + threadId);
    // 计算写入次数
    auto write_num = buffer.size() / kWriteSize;
    f.seek(offset);

    for (int i = 0; i < write_num; ++i)
    {
        f.write(buffer.data() + i * kWriteSize, kWriteSize);
    }

    auto remaining = buffer.size() - kWriteSize * write_num;

    if (remaining != 0)
    {
        f.write(buffer.data() + kWriteSize * write_num, remaining);
    }
    qDebug() << "thread ok" << threadId << ", time:" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    auto start = std::chrono::high_resolution_clock::now(); // 记录开始时间

    // 打开文件，准备写入
#if STL_STREAM
    std::ofstream of("Z:\\temp\\large_file.bin", std::ios::binary | std::ios::trunc);
    if (!of)
    {
        qDebug() << "无法打开文件进行写入。";
        return 1;
    }
#else
    QFile file("Z:\\temp\\large_file.bin");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        qDebug() << "无法打开文件进行写入。";
        return 1;
    }
#endif
    // 每个线程需要写入的数据量
    auto perThreadSize = size_t(std::ceil(kTotalSize * 1.0 / kThreadNum));
    auto remaining = kTotalSize % kThreadNum;

    // 创建线程
    std::vector<std::thread> threads;
    size_t offset = 0;
    for (int i = 0; i < kThreadNum; ++i)
    {
        if (i == kThreadNum - 1 && remaining != 0)
        {
            auto thread_size = kTotalSize - kThreadNum * perThreadSize;
#if STL_STREAM
            threads.emplace_back(writeThread, std::ref(of), offset, thread_size, i);
#else
            threads.emplace_back(writeThreadQt, std::ref(file), offset, thread_size, i);
#endif
        }
        else
        {
#if STL_STREAM
            threads.emplace_back(writeThread, std::ref(of), offset, perThreadSize, i);
#else
            threads.emplace_back(writeThreadQt, std::ref(file), offset, perThreadSize, i);
#endif
            offset += perThreadSize;
        }
    }

    for (auto &t : threads)
    {
        t.join(); // 等待所有线程完成写入
    }

#if STL_STREAM
    of.close();
#else
    file.close();
#endif
    auto end = std::chrono::high_resolution_clock::now(); // 记录结束时间
    std::chrono::duration<double> diff = end - start;
    qDebug() << "文件写入成功，耗时: " << diff.count() << " 秒";

    return a.exec();
}
